/*
 * CanvasPie - a canvas pie grapher (licensed under GNU GPL v3)
 *
 * Give the CanvasPie object your canvas object, array of information, and key 
 * object (div).  Optionally tell it to draw the graph/key later (when you tell 
 * it to).  Graph automatically scales with height/width of canvas object.
 * 
 */

function CanvasPie(canvas_element, array_in, key_element, draw_later)
{
	this.canvas_context = canvas_element.getContext("2d");
	this.array = array_in;
	this.key_element = key_element;

	//['Pies', 50, 'blue'];
	this.TITLEINDEX = 0;
	this.TOTALINDEX = 1;
	this.COLORINDEX = 2;

	this.MIN_FONTSIZE = 10;
	this.MIN_KEYHEIGHT = 10;
	
	this.default_colors_array = ['green', 'blue', 'orange', 'purple', 'red', 'yellow', 'violet', 'gold', 'magenta', 'brown', 'chartreuse', 'crimson', 'cyan', 'darkkhaki', 'firebrick', 'honeydew', 'lightblue', 'lightgreen', 'navy', 'yellowgreen', 'aliceblue', 'beige', 'cadetblue', 'darkgoldenrod', 'darkgrey', 'orchid', 'darkolivegreen', 'deeppink', 'indigo', 'lavendar']; //30 default colors.  if you're too lazy to specify and have more than 30 entries, perhaps a pie graph is not for you
	
	if(!draw_later)
	{
		draw_later = 0;
		this.drawGraph();
		this.drawKey();
	}
	
}

//draw the key which links colors to titles, shows %'s
//key scales to size of canvas
//must be called after drawGraph()
CanvasPie.prototype.drawKey = function()
{
	var ct = this.array.length;
	
	for(i=0; i<ct; i++)
	{
		if(this.array[i][this.TITLEINDEX])
		{
		var parent_div = document.createElement('div');
		parent_div.style.clear = "both";
		
		var box_div = document.createElement('div');
		var label_div = document.createElement('div');
		
		box_div.style.cssFloat = "left";
		label_div.style.cssFloat = "left";
		box_div.style.backgroundColor = this.array[i][this.COLORINDEX];
		label_div.innerHTML = this.array[i][this.TITLEINDEX]+' ('+this.calculatePercentage(this.array[i][this.TOTALINDEX])+'%)';
		
		box_div.style.border = "1px solid black";
		
		
		//style boxes, labels based on size of graph
		
		var box_height = this.canvas_context.canvas.height < this.MIN_KEYHEIGHT ? this.MIN_KEYHEIGHT : Math.round(this.canvas_context.canvas.height / 100 * 5);
		
		box_div.style.height = box_height+"px";
		box_div.style.width = box_height+"px";
		
		parent_div.style.padding = Math.round(box_height / 2.5)+"px";
		
		label_div.style.padding = Math.round(box_height / 3.5)+"px";
		
		var label_font = Math.round(box_height / 1.67) < this.MIN_FONTSIZE ? this.MIN_FONTSIZE : Math.ceil(box_height / 1.67);
		
		label_div.style.fontSize = label_font+"px";
		
		
		parent_div.appendChild(box_div);
		parent_div.appendChild(label_div);
		
		var clear_div = document.createElement('div');
		clear_div.style.clear = "both";
		parent_div.appendChild(clear_div);
		
		this.key_element.appendChild(parent_div);
		}
	}

}

CanvasPie.prototype.calculatePercentage = function(n)
{
	return Math.round(n/this.total * 10000)/100;
}

CanvasPie.prototype.drawGraph = function()
{
	this.calculatePoints();
	this.calculateTotal();
	this.drawCircle();
	this.drawParts();
}

CanvasPie.prototype.drawArcAtIndexWithAngles = function(i, calculated_angle, current_angle)
{
	this.canvas_context.beginPath();

	this.canvas_context.fillStyle = this.array[i][this.COLORINDEX];

	//handle the curved parts
	this.canvas_context.arc(this.center_x, this.center_y, this.radius, this.degreesToRadians(current_angle), this.degreesToRadians(calculated_angle + current_angle), false);
		
	this.canvas_context.fill();
}

CanvasPie.prototype.drawTriangleAtIndexWithAngles = function(i, current_angle, last_angle)
{
	var angle1, angle2;
	
	angle1 = this.degreesToRadians(360 + last_angle);
	angle2 = this.degreesToRadians(360 + current_angle);
	
	this.canvas_context.beginPath();

	//draw the triangles
	//use ((cos t) * radius, sin t * radius) to calculate appropriate points along circle to draw lines to.  add the cos/sin to ellimate space between triangle and semicircle
	//leaves a slight point outside the circle sometimes...
				
	//x
	x1 = (this.radius * Math.cos(angle1)) + Math.cos(angle1);
	x2 = (this.radius * Math.cos(angle2)) + Math.cos(angle2);
		
	//y
	y1 = (this.radius * Math.sin(angle1)) + Math.sin(angle1);
	y2 = (this.radius * Math.sin(angle2)) + Math.sin(angle2);
		
	this.canvas_context.moveTo(this.center_x, this.center_y);	
	this.canvas_context.lineTo((this.center_x + x1), (this.center_y+y1));
	this.canvas_context.lineTo((this.center_x + x2), (this.center_y+y2));
	this.canvas_context.fill();
}

CanvasPie.prototype.drawParts = function()
{
	var current_angle = 0;
	var calculated_angle;
	var last_angle;
	var x1, x2, y1, y2;
	
	var ct = this.array.length;
	
	for(i=0; i<ct; i++)
	{
		calculated_angle = (this.array[i][this.TOTALINDEX] / this.total) * 360;
	
		if(calculated_angle < 180)
		{
			this.drawArcAtIndexWithAngles(i, calculated_angle, current_angle);

			current_angle += calculated_angle;
			last_angle = current_angle - calculated_angle;
		
			this.drawTriangleAtIndexWithAngles(i, current_angle, last_angle);
		
		}
		else
		{
			//only one of these, so we move it to the front of the array
			//so we can draw a large arc now and just draw new pieces over it
			this.drawArcAtIndexWithAngles(i, calculated_angle, current_angle);

			current_angle += calculated_angle;
		}
	}
}

CanvasPie.prototype.degreesToRadians = function(d)
{
	return (Math.PI/180)*d;
}

CanvasPie.prototype.drawCircle = function()
{
	this.canvas_context.beginPath();
	this.canvas_context.arc(this.center_x, this.center_y, this.radius, 0, (Math.PI*2), false);
	
	this.canvas_context.stroke();
}

//calculates total number of entries for each slice
CanvasPie.prototype.calculateTotal = function()
{
	var default_color_index = 0;
	var ct = this.array.length;
	this.total = 0;
	for(i=0; i<ct; i++)
	{
		this.total += this.array[i][this.TOTALINDEX];
		
		//fill in default colors
		if(!this.array[i][this.COLORINDEX])
		{
			if(default_color_index >= this.default_colors_array.length)
			{
				//exhausted default colors array... suffer the consequences
				this.array[i][this.COLORINDEX] = 'white';
			}
			else
			{
				this.array[i][this.COLORINDEX] = this.default_colors_array[default_color_index];
			
				default_color_index++;
			}
		}
	}
	
	//move any slices >.5 to the front of the array
	for(i=0; i<ct; i++)
	{
		if(this.array[i][this.TOTALINDEX] / this.total > 0.5)
		{
			var array_holder = new Array();
			
			array_holder[this.TITLEINDEX] = this.array[i][this.TITLEINDEX];
			array_holder[this.TOTALINDEX] = this.array[i][this.TOTALINDEX];
			array_holder[this.COLORINDEX] = this.array[i][this.COLORINDEX];
			
			this.array.splice(i, 1);
			
			this.array.unshift(array_holder);
		}
	}
}

//calculates center of circle, radius
CanvasPie.prototype.calculatePoints = function()
{
	var smallest_dimension = this.canvas_context.canvas.width < this.canvas_context.canvas.height ? this.canvas_context.canvas.width : this.canvas_context.canvas.height;
	
	this.radius = (smallest_dimension/2)-1;//subtract 1 so the stroke doesn't go out of canvas bounds

	this.center_x = smallest_dimension / 2;
	this.center_y = smallest_dimension / 2;
}